前面 Day 02 到 Day 05 我們先簡單地介紹了 Kubernetes 想解決的幾個基本問題與其作法,如 controller 會以控制迴路(Control Loop)持續讓實際狀態靠近宣告的 spec;Deployment、Service 與 Ingress 分別負責工作負載、服務發現與 HTTP 路由;Kustomize 讓共用設定和環境差異不必混在同一份 YAML;CRD 則讓團隊能用 Microservice 這類自定義資源表達服務意圖。
不過,這些資源回答的是「叢集應該維持什麼狀態」,還沒有回答另一個交付問題:哪一份宣告才是某個環境現在應該採用的版本?
假設 Todo 團隊在週五晚上修正了建立待辦事項偶爾逾時的問題,並直接在 Production 執行 kubectl set image。修正當下生效了,但 Git 裡仍是舊 image。下次重建環境、處理事故,或有人想知道這個版本為什麼進入 Production 時,團隊該相信終端機歷史、CI log,還是叢集裡目前的資源?
GitOps 要處理的正是這件事:把環境的期望狀態放進可審查、可追溯且可重建的 Git History 內,並由叢集內的 controller 依照這份宣告維持狀態。
把 Kubernetes YAML 放進 Git 是好習慣,但它可能只是一份備份。GitOps 至少有三個條件:
這裡的「單一真實來源(Single Source of Truth)」是指環境應該變成什麼樣子,不是 Git 和 Kubernetes 在每一個瞬間都完全相同。Kubernetes 保存觀察到的狀態;GitOps controller 讀取 Git 的宣告、比對叢集現況,再執行 reconcile。
Git:環境的期望狀態
Kubernetes:環境的觀察結果
GitOps controller:比對兩者,依政策處理差異
因此,GitOps 不是用 Git 取代 Kubernetes。Git 提供可審查的變更歷史,Kubernetes controller 仍負責讓資源狀態收斂;GitOps controller 則把兩者接起來。
傳統部署常採用 Push-based 流程:CI 完成 build 與測試後,使用 kubeconfig 或 cloud credential 直接呼叫 Kubernetes API。

這種方式可以運作,但 CI runner 必須取得能改動目標叢集的憑證。若多條 pipeline 都能部署到 Production,就必須分別管理它們的權限、憑證輪替與稽核紀錄。
GitOps 常採用 Pull-based 流程。CI 仍負責驗證原始碼並產出 container image,但部署端不再由 CI 直接操作叢集。CI 將要部署的版本更新為 Git 的一筆 commit 或 Pull Request;常駐在叢集內的 controller 主動拉取已核准的設定,再用自己受限的 Kubernetes 權限同步資源。

兩種模式的差異不只是誰先發出請求,而是信任邊界的位置:
這不代表 Pull-based 天生安全。controller 若擁有過大的 Kubernetes 權限,或設定 repository 沒有保護分支與 review,風險仍然存在。GitOps 是把權限拆開,讓團隊能分別治理,而不是自動消除權限問題。
先把兩種 commit 的責任分清楚。原始碼變更描述的是應用程式如何運作;環境設定變更描述的是哪個已知版本要在哪個環境執行,以及要維持哪些 Kubernetes 資源。
以 Todo 系統為例,可以把它們視為兩個邏輯範圍:
應用程式原始碼
Account、Todo、BFF、Web 的程式碼、測試與 Dockerfile
環境設定
各環境的 Kubernetes manifest、Kustomize overlay 與 GitOps controller 的部署宣告
這不強制要求一定要拆成兩個 repository。mono-repo 同樣可以做到,前提是 review、目錄邊界與權限能分辨兩種變更。重點是不要讓「程式碼已通過測試」自動等同於「這個 image 已被批准進入某個環境」。
假設 Staging 要從 0.1.0 升級到 0.1.1,設定變更應該明確指出 image reference 的變化。reviewer 能直接判斷升級範圍;若部署造成問題,也能回到這一筆 commit 還原或追查。部署版本不該由 CI 在執行當下隱含決定。
環境設定也不應保存明文 password、token 或 private URL。Git 可以保存 Secret 的名稱、External Secret 的 reference,或經團隊核准的加密資料;真正的敏感值仍應交給專門的 Secret 管理機制處理。
GitOps 最容易看見的情況是設定漂移(drift)。例如 Git 宣告 todo-api 在 Production 應維持三個副本,但有人為了排查問題,手動把它調成五個,就會變成:
Git 的期望副本數:3
叢集觀察到的副本數:5
-> 發生 drift
controller 可以偵測這個差異,但如何處理不是技術自動能替團隊決定的事。自動回復能維持一致性,卻也可能覆蓋事故處理中的臨時操作。團隊至少要先定義:
若手動調整是暫時的,最後仍應回到 Git,或明確撤銷它。否則下一次同步、重建或部署時,環境很可能在沒有人察覺的情況下回到舊宣告。
把期望狀態留在 Git,最大的價值不只是「可以看 diff」。當環境需要重建,團隊不必依賴某位工程師記得最後執行過哪些命令;當部署異常,也能沿著設定的 commit、review 與 controller 事件追查。
不過,Git 本身不會讀取 commit,更不會將 YAML 套用到 Kubernetes。下一篇會使用 Argo CD 作為 GitOps controller,讓 Git 的環境宣告能持續同步到叢集,並區分資源已同步與服務真正健康這兩種狀態。